using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using FluentMetadata.Attributes;

namespace FluentMetadata.MvcIntegration
{
    public class FluentMetadataModelValidator : ModelValidator
    {
        private ICustomTypeDescriptor _descriptor;

        public FluentMetadataModelValidator(ModelMetadata metadata, ControllerContext controllerContext, ICustomTypeDescriptor descriptor)
            : base(metadata, controllerContext)
        {
            _descriptor = descriptor;
        }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            var property = (from prop in _descriptor.GetProperties().Cast<PropertyDescriptor>()
                            where prop.Name == Metadata.PropertyName
                            select prop).SingleOrDefault();

            var attributes = property.Attributes.OfType<ValidationAttribute>();

            foreach (var attr in attributes)
            {
                if (attr.GetType() == typeof(RequiredAttribute))
                {
                    yield return new ModelClientValidationRequiredRule(attr.FormatErrorMessage(property.Name));
                } else if(attr.GetType() == typeof(StringLengthAttribute))
                {
                    yield return
                        new ModelClientValidationStringLengthRule(attr.FormatErrorMessage(property.Name),
                                                                  ((StringLengthAttribute) attr).MinimumLength,
                                                                  ((StringLengthAttribute) attr).MaximumLength);
                } else if(attr.GetType() == typeof(RangeAttribute))
                {
                    yield return
                        new ModelClientValidationRangeRule(attr.FormatErrorMessage(property.Name),
                                                           ((RangeAttribute) attr).Minimum,
                                                           ((RangeAttribute) attr).Maximum);
                } else if(attr.GetType() == typeof(RegularExpressionAttribute))
                {
                    yield return
                        new ModelClientValidationRegexRule(attr.FormatErrorMessage(property.Name),
                                                           ((RegularExpressionAttribute) attr).Pattern);
                } else if(attr.GetType() == typeof(DataTypeWrapper))
                {
                    var innerAttr = (DataTypeWrapper) attr;

                    switch(innerAttr.DataType)
                    {
                        case DataType.EmailAddress:
                            yield return
                                new ModelClientValidationRegexRule(attr.FormatErrorMessage(property.Name),
                                                                   DataTypeWrapper.EmailAddressRegex);
                            break;
                        case DataType.Url:
                            yield return
                                new ModelClientValidationRegexRule(attr.FormatErrorMessage(property.Name),
                                                                   DataTypeWrapper.UrlRegex);
                            break;
                    }
                }
            }
        }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            //As a test!
            var model = Metadata.Model ?? container;

            var validationContext = new ValidationContext(container, null, null);

            var properties = from prop in _descriptor.GetProperties().Cast<PropertyDescriptor>()
                             select prop;

            foreach (var property in properties)
            {
                var propertyValue = property.GetValue(container);

                //Get ValidationAttributes for property
                var attributes = property.Attributes.OfType<ValidationAttribute>();

                foreach (var attr in attributes)
                {
                    //Validate property.
                    var validationResult = attr.GetValidationResult(propertyValue, validationContext);

                    if (validationResult != ValidationResult.Success)
                    {
                        yield return
                            new ModelValidationResult
                                {
                                    MemberName = property.Name,
                                    Message = attr.FormatErrorMessage(property.Name)
                                };
                    }
                }
            }
        }
    }
}